<?php

/**
 * ERP专用类
 * 继承框架的Controller，service，dao等类
 */
require_once(rtrim(dirname(dirname(__FILE__)), '/').'/initphp/initphp.php');

/**
 * 
 * 控制器
 * @author iceman
 *
 */
class iceController extends Controller {
	
}

/**
 * 
 * 服务类
 * 1. service主要用于同时操作多个相关 Dao，完成业务逻辑。
 * 2. 读数据时，从视图中获得；写数据时，由dao完成
 * 3. 业务表允许有"一主多从"，但是，servie只允许"一主一从"，故，需要将"一主多从"拆为多个"一主一从"
 * 
 * @author iceman
 * 
 */
class iceService extends Service {
	
	/**
	 * Dao对象
	 * @var daoInit
	 */
	protected $dao;
	
	/**
	 * 功能设想：
	 * 1）为了拓展性，将一个业务表拆分为多个分表（多个分表dao+一个试图dao），需要 service统一管理
	 * 2）数据表存在“主-从”关系，需要servier统一管理
	 * 3）读数据，从视图dao；写数据，用分表dao
	 */
	
	/**
	 * 数据表（多张相关的分表，只取主名，忽略扩展）
	 * 例如： master_table_name = 'order'，相关数据表为 order_base, order_extend 等
	 * slave_table_name 如果有，必须与 master一一对应。 即使 master 对应多个  slave ，也必须拆分成多个 service 处理
	 * @var string
	 */
	protected $_master_table_name = '';
	protected $_slave_table_name = '';
	/**
	 * 数据表主键（此处特指具有自增属性的主键，此主键可在多张相关表中出现）
	 */
	protected $_master_table_pk = '';
	protected $_slave_table_pk = '';
	/**
	 * 外键（与本库其他表的关联）
	 * 形如 array(
	 * 		'xid' => array( //视图中的字段
	 * 			'table' => 'customer', //关联数据表
	 * 			'field' => 'customer_id', //关联字段
	 * 			'return' => array('customer_name') //返回字段
	 * 		)
	 * )
	 * 字段名称对应一个数组，数组3个元素分别为：数据表名，对应字段名，显示字段名
	 * 隐含一种默认情况，主-分表ID，自动对应，不需要额外指定。
	 */
	protected $_master_table_relations = array();
	protected $_slave_table_relations = array();
	/**
	 * 数据视图
	 */
	protected $_master_view = '';
	protected $_slave_view = '';
	/**
	 * 如果视图不存在，是否自动建立
	 */
	protected $_auto_check_view = FALSE; //是否检测视图是否存在
	protected $_auto_create_view = TRUE; //是否自动创建视图，如果不存在
	/**
	 * 分表扩展名（不管是否用得到，所有分表都必须存在）
	 * base 基本信息，如：ID等
	 * extend 扩展信息，如：与其他表的关联信息等
	 * spec 特殊信息（定制信息），如：备注类字段
	 * status 状态信息，如：是否删除
	 */
	protected $_table_exts = array('base', 'extend', 'spec', 'status');
	
	/*******************************系统底层功能begin****************************************/
	/**
	 * 系统级功能:消息，标签，工作流，权限，多语言
	 */
	protected $_sys_tag = 'base_tag_name';
	protected $_sys_tag_ref = 'base_tag_reference';

	//插入标签
	public function insert_tag($tagname = '', $hash = '')
	{
		
	}
	//删除标签（删除引用，并不删除消息本身）
	public function delete_tag($hash = '')
	{
		
	}
	//获取标签
	public function get_tag($what = '', $who = '')
	{
		
	}
	
	
	
	protected $_sys_msg = '';
	//插入消息
	//删除消息
	//获取消息
	
	/*******************************系统底层功能end****************************************/

	public function __construct()
	{
		parent::__construct();
		
		$this->dao = $this->load('dao', 'd'); //导入D
		$this->dao->run_db(); //初始化db
		$this->dao->run_cache(); //初始化cahce
	}
	
	/**
	 * 获得视图名称
	 * 如果视图不存在，则创建之。如果创建失败，直接退出。
	 */
	public function _build_view($tablename = '', $viewname = '')
	{
		if ($this->_auto_check_view == FALSE)	return TRUE;
		if (empty($tablename) || empty($viewname) || empty($this->_table_exts))	return FALSE;
		
		$sql = 'show tables';
		$result = $this->dao->db->query($sql);
		while (($row = $this->dao->db->fetch_assoc($result)) != FALSE)
		{
			if ($viewname == current($row))	return TRUE;
		}

		if ($this->_auto_create_view == FALSE)	InitPHP::initError('View is not existed : '.$viewname);
		
		$basetable = $tablename.'_base';
		$key_id = ($tablename == $this->_master_table_name) ? $this->_master_table_pk : $this->_slave_table_pk;

		$tables = array();
		foreach ($this->_table_exts as $v)
		{
			$tables[] = $tablename.'_'.$v;
		}
		
		$finalsql = 'CREATE VIEW '.$viewname.' AS ';
		$finalsql .= 'SELECT '.$basetable.'.'.$key_id;
		foreach ($tables as $t)
		{
			$sql = 'show columns from '.$t;
			$result = $this->dao->db->query($sql);
			while (($row = $this->dao->db->fetch_assoc($result)) != FALSE)
			{
				if ($row['Field'] == $key_id)	continue;

				$finalsql .= ', '.$t.'.'.$row['Field'];
			}
		}
		$finalsql .= ' FROM '.implode(',', $tables);
		$finalsql .= ' WHERE 1 ';
		foreach ($tables as $t)
		{
			if ($t == $basetable)	continue;
			
			$finalsql .= ' AND '.$t.'.'.$key_id.'='.$basetable.'.'.$key_id;
		}
		$res = $this->dao->db->query($finalsql);
		
		return $res;
	}
	private function _get_master_view_name()
	{
		if (empty($this->_master_view))
		{
			$this->_master_view = $this->_master_table_name . '_view';
		}
		$this->_build_view($this->_master_table_name, $this->_master_view);
		
		return $this->_master_view;
	}
	private function _get_slave_view_name()
	{
		if (empty($this->_slave_view))
		{
			$this->_slave_view = $this->_slave_table_name . '_view';
		}
		$this->_build_view($this->_slave_table_name, $this->_slave_view);
		
		return $this->_slave_view;
	}
	
	/**
	 * 通用操作函数，master 和 slave 相关函数都是调用这些函数
	 */
	private function _insert_row($type = 'master', $data = array())
	{
		//根据type确定是master还是slave
		$table_name = $type == 'master' ? $this->_master_table_name : $this->_slave_table_name;
		$key_name = $type == 'master' ? $this->_master_table_pk : $this->_slave_table_pk;
		
		$daos = array();
		foreach ($this->_table_exts as $v)
		{
			$daos[$v] = InitPHP::getDao($table_name.'_'.$v);
		}
		
		//先新增base，获得insert_id
		$id = $daos['base']->insert_row($data);
		if (empty($id))	return FALSE;
		
		$data[$key_name] = $id;
		unset($daos['base']);
		
		//再增加其他的信息
		$res = array();
		foreach ($daos as $k => $dao)
		{
			$res[$k] = $dao->insert_row($data);
		}
		
		//如果增加不成功，或许应该把已经增加的所有记录全部删除
		if (array_sum($res) != count($daos))
		{
			$this->_delete_rows($type, $id, $key_name);
			
			return FALSE;
		}
		
		return $id;
	}
	private function _update_row($type = 'master', $data = array(), $key_id = 0)
	{
		//根据type确定是master还是slave
		$table_name = $type == 'master' ? $this->_master_table_name : $this->_slave_table_name;
		$key_name = $type == 'master' ? $this->_master_table_pk : $this->_slave_table_pk;
		
		$daos = array();
		foreach ($this->_table_exts as $v)
		{
			$daos[$v] = InitPHP::getDao($table_name.'_'.$v);
		}
		
		//不可更新主键
		unset($data[$key_name]);
		
		$res = array();
		foreach ($daos as $k => $dao)
		{
			$res[$k] = $dao->update_row($data, $key_id, $key_name);
		}
		
		return array_sum($res) == count($daos);
	}
	private function _delete_row($type = 'master', $key_id = 0)
	{
		//根据type确定是master还是slave
		$table_name = $type == 'master' ? $this->_master_table_name : $this->_slave_table_name;
		$key_name = $type == 'master' ? $this->_master_table_pk : $this->_slave_table_pk;
		
		$daos = array();
		foreach ($this->_table_exts as $v)
		{
			$daos[$v] = InitPHP::getDao($table_name.'_'.$v);
		}
		
		$res = array();
		foreach ($daos as $k => $dao)
		{
			$res[$k] = $dao->delete_row($key_id, $key_name);
		}
		
		//删除master时，顺带删除所有slave
		if ($type == 'master' && ! empty($this->_slave_table_name))
		{
			$this->_delete_rows('slave', $key_id, $key_name);
		}
		
		return array_sum($res) == count($daos);
	}
	private function _delete_rows($type = 'master', $key_id = 0, $key_name = '')
	{
		if (empty($key_name) || empty($key_id))	return FALSE;
		
		$table_name = $type == 'master' ? $this->_master_table_name : $this->_slave_table_name;
		$pk_name = $type == 'master' ? $this->_master_table_pk : $this->_slave_table_pk;

		//获得所有的id
		$dao = InitPHP::getDao($table_name.'_base');
		
		$ids = array();
		$data = $dao->get_rows(array($key_name => $key_id), 1, 1000000);
		if (! empty($data))
		{
			foreach ($data as $v)
			{
				$ids[] = $v[$pk_name];
			}
		}
		if (empty($ids))	return FALSE;
		
		$daos = array();
		foreach ($this->_table_exts as $v)
		{
			$daos[$v] = InitPHP::getDao($table_name.'_'.$v);
		}
		
		$res = array();
		foreach ($daos as $k => $dao)
		{
			$res[$k] = $dao->delete_row($ids, $pk_name);
		}
		
		return array_sum($res) == count($daos);
	}
	private function _get_row($type = 'master', $key_id = 0)
	{
		//根据type确定是master还是slave
		$table_name = $type == 'master' ? $this->_master_table_name : $this->_slave_table_name;
		$view_name = $type == 'master' ? $this->_get_master_view_name() : $this->_get_slave_view_name();
		$key_name = $type == 'master' ? $this->_master_table_pk : $this->_slave_table_pk;
		
		$dao = InitPHP::getDao($view_name);
		$res = $dao->get_row($key_id, $key_name);
		
		$res = $this->_append_relations($type, array($res));
		$res = current($res);

		return $res;
	}
	private function _get_rows($type = 'master', $data = array(), $pagenum = 1, $pagesize = 10, $order_field = '', $sort = 'DESC')
	{
		//根据type确定是master还是slave
		$table_name = $type == 'master' ? $this->_master_table_name : $this->_slave_table_name;
		$view_name = $type == 'master' ? $this->_get_master_view_name() : $this->_get_slave_view_name();
		$key_name = $type == 'master' ? $this->_master_table_pk : $this->_slave_table_pk;
		
		$dao = InitPHP::getDao($view_name);
		$res = $dao->get_rows($data, $pagenum, $pagesize, $order_field, $sort);
		
		$res = $this->_append_relations($type, $res);
		
		return $res;
	}
	
	/**
	 * 操作主表
	 */
	public function insert_master_row($data = array())
	{
		return $this->_insert_row('master', $data);
	}
	public function update_master_row($data = array(), $key_id = 0)
	{
		return $this->_update_row('master', $data, $key_id);
	}
	public function delete_master_row($key_id = 0)
	{
		return $this->_delete_row('master', $key_id);
	}
	public function get_master_row($key_id = 0)
	{
		return $this->_get_row('master', $key_id);
	}
	public function get_master_rows($data = array(), $pagenum = 1, $pagesize = 10, $order_field = '', $sort = 'DESC')
	{
		return $this->_get_rows('master', $data, $pagenum, $pagesize, $order_field, $sort);
	}
	
	/**
	 * 操作从表
	 */
	public function insert_slave_row($data = array())
	{
		return $this->_insert_row('slave', $data);
	}
	public function update_slave_row($data = array(), $key_id = 0)
	{
		return $this->_update_row('slave', $data, $key_id);
	}
	public function delete_slave_row($key_id = 0)
	{
		return $this->_delete_row('slave', $key_id);
	}
	public function get_slave_row($key_id = 0)
	{
		return $this->_get_row('slave', $key_id);
	}
	public function get_slave_rows($data = array(), $pagenum = 1, $pagesize = 10, $order_field = '', $sort = 'DESC')
	{
		return $this->_get_rows('slave', $data, $pagenum, $pagesize, $order_field, $sort);
	}
	
	/**
	 * 增加关联表处理
	 */
	private function _append_relations($type = 'master', $data = array())
	{
		if (empty($data))	return $data;
		
		$relation_name = $type == 'master' ? $this->_master_table_relations : $this->_slave_table_relations;
		if (empty($relation_name))	return $data;
		
		//确定已查询到结果的第一行元素是否满足关联表
		$first = current($data);
		foreach ($relation_name as $k => $v)
		{
			if (! isset($first[$k]) || 
				empty($v['table']) || empty($v['field']) || empty($v['return']) || ! is_array($v['return']))
			{
				unset($relation_name[$k]);
			}
		}
		if (empty($relation_name))	return $data;

		//获得所有id
		$ids = array();
		foreach ($data as $v)
		{
			foreach ($relation_name as $m => $n)
			{
				$ids[$m][] = $v[$m];
			}
		}
		
		//根据id获得所有的返回字段
		$res = array();
		foreach ($relation_name as $k => $v)
		{
			$v['return'] = array_unique(array_push($v['return'], $v['field']));
			
			$ids[$k] = array_unique($ids[$k]);
			
			$sql = 'select '.implode(',', $v['return']).
					' from '.$v['table'].
					' where '.$v['field'].$this->dao->db->build_in($ids[$k]);
			$rows = $this->dao->db->get_all_sql($sql);
			if (! empty($rows))
			{
				foreach ($rows as $row)
				{
					$res[$k][$row[$v['field']]] = $row;
				}
			}
		}
		
		//合并到data
		foreach ($data as $k => $v)
		{
			foreach ($relation_name as $m => $n)
			{
				$v[$m.'_relation'] = $res[$m][$v[$m]];
			}
			
			$data[$k] = $v;
		}
		
		return $data;
	}
}

/**
 * 
 * 模型类
 * 1. 函数仅对外提供参数级调用（即：传入参数，获得结果），不提供代码级调用（即：不直接执行SQL语句）
 * 2. dao的命名，为“数据表+Dao”
 * 
 * @author iceman
 *
 */
class iceDao extends Dao {
	
	/**
	 * 字段类型列表（不常用类型不予显示）
	 * @author iceman
	 */
	protected $_field_types = array(
		'tinyint', 'smallint', 'int', 'bigint',
		'double',
		'boolean',
		'enum',	
		'char', 'varchar',
		'text', 'longtext',
		'year', 'date', 'time', 'datetime'
	);
	/**
	 * 数据表名称
	 */
	protected $_table_name = '';
	/**
	 * 数据表主键（此处特指具有自增属性的主键）
	 */
	protected $_table_pk = '';
	/**
	 * 数据表字段，需手动维护（此处字段数量，可少于实际数据表字段，因某些字段不需要显示）
	 * 数据表字段的显示名称，考虑以语言文件或语言表形式进行国际化
	 * 形如 array(
	 * 	'xx1' => array(
	 * 		'type' => 'varchar', //字段类型
	 * 		'must' => true, //是否必须。当必须，而参数中又没有时，自动使用 default
	 * 		'default' => '' //默认值
	 * ))
	 * 其中，字段类型、默认值可从数据表中获得（如下），也可自行指定（如日期默认为当前时间）
	 * 以上3个字段是必须，也允许自定义字段，以方便扩展
	 * @var array()
	 */
	protected $_table_fields = array();
	/**
	 * 数据表字段验证规则
	 * 形如 array(
	 * 	'xx' => array('int', 'value:100~900'),
	 * 	'xx' => array('email','length:10~50')
	 * )
	 * 第一个
	 */
	protected $_table_rules = array();

	
	/**
	 * 参数验证
	 */
	private function _validate_params($data = array())
	{
		if (empty($data) || empty($this->_table_rules))	return $data;
		
		$util = $this->getUtil('fieldvalidate'); //加载字段验证util
		if (empty($util))	return $data;

		foreach ($data as $k => $v)
		{
			if (! empty($this->_table_rules[$k]))
			{
				$rule = $this->_table_rules[$k];
				
				foreach ($rule as $n)
				{
					if (in_array($n, $this->_field_types))
					{
						$r = $util->type_limit($v, $n);
						if (empty($r))	return FALSE;
					}
					else
					{
						$n = explode(':', $n);
						$func = strval($n[0]) . '_limit';
						if (method_exists($util, $func) == FALSE)	return FALSE;

						$r = $util->$func($v, $n[1]);
						if (empty($r))	return FALSE;
					}
				}
			}
		}
		
		return $data;
	}
	/**
	 * 获得所有字段，主要用于自动化显示前台的可输入框
	 * @author iceman
	 */
	public function get_fields()
	{
		return $this->_table_fields;
	}
	/**
	 * 过滤参数：1）不在数据表字段中的参数自动略过；2）在数据表字段中但不在参数中的，可自动补齐(可选)
	 * @author iceman
	 */
	private function _filter_fields($data = array())
	{
		if (empty($this->_table_fields))
		{
			return $data;
		}

		if (! empty($data))
		{
			foreach ($data as $k => $v)
			{
				if (! isset($this->_table_fields[$k]))
				{
					unset($data[$k]);
				}
			}
		}
		
		foreach ($this->_table_fields as $k => $v)
		{
			if (! isset($data[$k]) && (isset($v['must']) && $v['must'] == TRUE))
			{
				$data[$k] = $v['default'];
			}
		}
		
		return $data;
	}
	
	
	/**
	 * 新增一条记录
	 * @author iceman
	 */
	public function insert_row($data = array())
	{
		$data = $this->_filter_fields($data);
		$data = $this->_validate_params($data);
		if (empty($data))	return FALSE;
		
		return $this->dao->db->insert($data, $this->_table_name);
	}
	/**
	 * 更新一条记录
	 * @author iceman
	 */
	public function update_row($data = array(), $key_id = 0, $key_name = '')
	{
		$data = $this->_filter_fields($data);
		$data = $this->_validate_params($data);
		if (empty($data))	return FALSE;
		
		$key_name = ! empty($key_name) ? $key_name : $this->_table_pk;
		
		return $this->dao->db->update($key_id, $data, $this->_table_name, $key_name);
	}
	/**
	 * 删除一条记录
	 * @author iceman
	 */
	public function delete_row($key_id = 0, $key_name = '')
	{
		$key_name = ! empty($key_name) ? $key_name : $this->_table_pk;
		
		return $this->dao->db->delete($key_id, $this->_table_name, $key_name);
	}
	/**
	 * 获取一条记录
	 * @author iceman
	 */
	public function get_row($key_id = 0, $key_name = '')
	{
		$key_name = ! empty($key_name) ? $key_name : $this->_table_pk;
		
		return $this->dao->db->get_one($key_id, $this->_table_name, $key_name);
	}
	/**
	 * 获得若干记录
	 * @author iceman
	 */
	public function get_rows($data = array(), $pagenum = 1, $pagesize = 10, $order_field = '', $sort = 'DESC')
	{
		$data = $this->_filter_fields($data);
		$data = $this->_validate_params($data);
		
		$pagenum = intval($pagenum);
		$pagenum = $pagenum > 1 ? $pagenum : 1;
		
		$pagesize = intval($pagesize);
		$pagesize = $pagesize > 1 ? $pagesize : 10;
		
		$order_field = ! empty($order_field) ? $order_field : $this->_table_pk;
		
		return $this->dao->db->get_all($this->_table_name, $pagesize, ($pagenum-1)*$pagesize, $data, $order_field, $sort);
	}
}